/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.cs.bd.ad.self.timer;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.SystemClock;
import android.util.Log;

import com.cs.bd.utils.LogUtils;

/**
 * {@link TrustedTime} that connects with a remote NTP server as its trusted
 * time source.
 *
 * @hide
 */
public class NtpTrustedTime implements TrustedTime {
    private static final String TAG = "NtpTrustedTime";
    private static final boolean LOGD = false;

    private static NtpTrustedTime sSingleton;
    private static Context sContext;

    private final long mTimeout;

    private ConnectivityManager mCM;

    private boolean mHasCache;
    private long mCachedNtpTime;
    private long mCachedNtpElapsedRealtime;
    private long mCachedNtpCertainty;

    private int mNtpIndex;

    private static final String[] mNTP_ARRAYS = {
            "ntp1.aliyun.com", "ntp2.aliyun.com", "ntp3.aliyun.com", "ntp4.aliyun.com", "ntp5.aliyun.com", "ntp6.aliyun.com", "ntp7.aliyun.com", "cn.pool.ntp.org",
            "dns1.synet.edu.cn",
            "news.neu.edu.cn",
            "dns.sjtu.edu.cn",
            "dns2.synet.edu.cn",
            "ntp.glnet.edu.cn",
            "ntp-sz.chl.la",
            "ntp.gwadar.cn",
            "cn.pool.ntp.org"
    };

    private NtpTrustedTime(long timeout) {
        mTimeout = timeout;
    }

    public static synchronized NtpTrustedTime getInstance(Context context) {
        if (sSingleton == null) {
            sSingleton = new NtpTrustedTime(5000);
            sContext = context.getApplicationContext();
        }
        return sSingleton;
    }

    @Override
    public boolean forceRefresh() {
        synchronized (this) {
            if (mCM == null) {
                mCM = (ConnectivityManager) sContext.getSystemService(Context.CONNECTIVITY_SERVICE);
            }
        }

        final NetworkInfo ni = mCM == null ? null : mCM.getActiveNetworkInfo();
        if (ni == null || !ni.isConnected()) {
            LogUtils.d("forceRefresh: no connectivity ");
            return false;
        }
        try {
            final SntpClient client = new SntpClient();
            String mServer = mNTP_ARRAYS[mNtpIndex % mNTP_ARRAYS.length];
            LogUtils.d("mServer : " + mServer);
            if (client.requestTime(mServer, (int) mTimeout)) {
                mHasCache = true;
                mCachedNtpTime = client.getNtpTime();
                mCachedNtpElapsedRealtime = client.getNtpTimeReference();
                mCachedNtpCertainty = client.getRoundTripTime() / 2;
                return true;
            }
        } catch (Exception e) {
        }
        mNtpIndex++;
        return false;
    }

    @Override
    public boolean hasCache() {
        return mHasCache;
    }

    @Override
    public long getCacheAge() {
        if (mHasCache) {
            return SystemClock.elapsedRealtime() - mCachedNtpElapsedRealtime;
        } else {
            return Long.MAX_VALUE;
        }
    }

    @Override
    public long getCacheCertainty() {
        if (mHasCache) {
            return mCachedNtpCertainty;
        } else {
            return Long.MAX_VALUE;
        }
    }

    @Override
    public long currentTimeMillis() {
        if (!mHasCache) {
            throw new IllegalStateException("Missing authoritative time source");
        }
        if (LOGD) Log.d(TAG, "currentTimeMillis() cache hit");

        // current time is age after the last ntp cache; callers who
        // want fresh values will hit makeAuthoritative() first.
        return mCachedNtpTime + getCacheAge();
    }

    public long getCachedNtpTime() {
        if (LOGD) Log.d(TAG, "getCachedNtpTime() cache hit");
        return mCachedNtpTime;
    }

    public long getCachedNtpTimeReference() {
        return mCachedNtpElapsedRealtime;
    }
}
